package com.dongliusuo.orangeforms.webadmin.app.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.alibaba.fastjson.JSONObject;
import com.dongliusuo.orangeforms.webadmin.app.dto.AreaCodeDto;
import com.dongliusuo.orangeforms.webadmin.app.model.AreaCode;
import com.dongliusuo.orangeforms.webadmin.app.service.AreaCodeService;
import com.dongliusuo.orangeforms.common.core.constant.ErrorCodeEnum;
import com.dongliusuo.orangeforms.common.core.util.MyModelUtil;
import com.dongliusuo.orangeforms.common.core.util.MyCommonUtil;
import com.dongliusuo.orangeforms.common.core.object.ResponseResult;
import com.dongliusuo.orangeforms.common.core.annotation.MyRequestBody;
import com.dongliusuo.orangeforms.common.core.validator.UpdateGroup;
import com.dongliusuo.orangeforms.common.log.annotation.OperationLog;
import com.dongliusuo.orangeforms.common.log.model.constant.SysOperationLogType;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import jakarta.validation.groups.Default;
import java.util.*;

/**
 * 国内行政区划操作控制器类。
 *
 * @author DongLiusuo
 * @date 2025-01-08
 */
@Tag(name = "国内行政区划管理接口")
@Slf4j
@RestController
@RequestMapping("/admin/app/areaCode")
public class AreaCodeController {

    @Autowired
    private AreaCodeService areaCodeService;

    /**
     * 新增国内行政区划数据。
     *
     * @param areaCodeDto 新增对象。
     * @return 应答结果对象，包含新增对象主键Id。
     */
    @ApiOperationSupport(ignoreParameters = {"areaCodeDto.areaId"})
    @SaCheckPermission("globalDict.update")
    @OperationLog(type = SysOperationLogType.ADD)
    @PostMapping("/add")
    public ResponseResult<String> add(@MyRequestBody AreaCodeDto areaCodeDto) {
        String errorMessage = MyCommonUtil.getModelValidationError(areaCodeDto);
        if (errorMessage != null) {
            return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
        }
        AreaCode areaCode = MyModelUtil.copyTo(areaCodeDto, AreaCode.class);
        AreaCode parentAreaCode;
        if (MyCommonUtil.isNotBlankOrNull(areaCode.getParentId())) {
            parentAreaCode = areaCodeService.getById(areaCode.getParentId());
            if (parentAreaCode == null) {
                errorMessage = "数据验证失败，关联的父节点并不存在，请刷新后重试！";
                return ResponseResult.error(ErrorCodeEnum.DATA_PARENT_ID_NOT_EXIST, errorMessage);
            }
        }
        areaCode = areaCodeService.saveNew(areaCode);
        return ResponseResult.success(areaCode.getAreaId());
    }

    /**
     * 更新国内行政区划数据。
     *
     * @param areaCodeDto 更新对象。
     * @return 应答结果对象。
     */
    @SaCheckPermission("globalDict.update")
    @OperationLog(type = SysOperationLogType.UPDATE)
    @PostMapping("/update")
    public ResponseResult<Void> update(@MyRequestBody AreaCodeDto areaCodeDto) {
        String errorMessage = MyCommonUtil.getModelValidationError(areaCodeDto, Default.class, UpdateGroup.class);
        if (errorMessage != null) {
            return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage);
        }
        AreaCode areaCode = MyModelUtil.copyTo(areaCodeDto, AreaCode.class);
        AreaCode originalAreaCode = areaCodeService.getById(areaCode.getAreaId());
        if (originalAreaCode == null) {
            return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
        }
        AreaCode parentAreaCode;
        if (MyCommonUtil.isNotBlankOrNull(areaCode.getParentId())
                && ObjectUtils.notEqual(areaCode.getParentId(), originalAreaCode.getParentId())) {
            parentAreaCode = areaCodeService.getById(areaCode.getParentId());
            if (parentAreaCode == null) {
                // NOTE: 修改下面方括号中的话述
                errorMessage = "数据验证失败，关联的 [父节点] 并不存在，请刷新后重试！";
                return ResponseResult.error(ErrorCodeEnum.DATA_PARENT_ID_NOT_EXIST, errorMessage);
            }
        }
        if (!areaCodeService.update(areaCode, originalAreaCode)) {
            return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
        }
        return ResponseResult.success();
    }

    /**
     * 删除国内行政区划数据。
     *
     * @param areaId 删除对象主键Id。
     * @return 应答结果对象。
     */
    @SaCheckPermission("globalDict.update")
    @OperationLog(type = SysOperationLogType.DELETE)
    @PostMapping("/delete")
    public ResponseResult<Void> delete(@MyRequestBody String areaId) {
        if (MyCommonUtil.existBlankArgument(areaId)) {
            return ResponseResult.error(ErrorCodeEnum.ARGUMENT_NULL_EXIST);
        }
        if (areaCodeService.hasChildren(areaId)) {
            // NOTE: 修改下面方括号中的话述
            String errorMessage = "数据验证失败，当前 [对象存在子对象] ，请刷新后重试！";
            return ResponseResult.error(ErrorCodeEnum.HAS_CHILDREN_DATA, errorMessage);
        }
        if (!areaCodeService.remove(areaId)) {
            return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST);
        }
        return ResponseResult.success();
    }

    /**
     * 白名单接口，登录用户均可访问。以字典形式返回全部国内行政区划数据集合。
     * 所有数据全部取自于缓存，对于数据库中存在，但是缓存中不存在的数据，不会返回。
     *
     * @return 应答结果对象，包含字典形式的数据集合。
     */
    @GetMapping("/listDict")
    public ResponseResult<List<Map<String, Object>>> listDict() {
        List<AreaCode> resultList = areaCodeService.getAllListFromCache();
        return ResponseResult.success(MyCommonUtil.toDictDataList(
                resultList, AreaCode::getAreaId, AreaCode::getAreaName, AreaCode::getParentId));
    }

    /**
     * 白名单接口，登录用户均可访问。以字典形式返回全部国内行政区划数据集合。
     * fullResultList中的字典列表全部取自于数据库，而cachedResultList全部取自于缓存，前端负责比对。
     *
     * @return 应答结果对象，包含字典形式的数据集合。
     */
    @GetMapping("/listAll")
    public ResponseResult<JSONObject> listAll() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("fullResultList", MyCommonUtil.toDictDataList(
                areaCodeService.getAllList(), AreaCode::getAreaId, AreaCode::getAreaName, AreaCode::getParentId));
        jsonObject.put("cachedResultList", MyCommonUtil.toDictDataList(
                areaCodeService.getAllListFromCache(), AreaCode::getAreaId, AreaCode::getAreaName, AreaCode::getParentId));
        return ResponseResult.success(jsonObject);
    }

    /**
     * 根据字典Id集合，获取查询后的字典数据。
     *
     * @param dictIds 字典Id集合。
     * @return 应答结果对象，包含字典形式的数据集合。
     */
    @GetMapping("/listDictByIds")
    public ResponseResult<List<Map<String, Object>>> listDictByIds(@RequestParam List<String> dictIds) {
        List<AreaCode> resultList = areaCodeService.getInList(new HashSet<>(dictIds));
        return ResponseResult.success(MyCommonUtil.toDictDataList(
                resultList, AreaCode::getAreaId, AreaCode::getAreaName, AreaCode::getParentId));
    }

    /**
     * 根据父主键Id获取其下级数据列表。
     * 白名单接口，登录用户均可访问。
     *
     * @param parentId 父主键Id。
     * @return 按照字典的形式返回下级数据列表。
     */
    @GetMapping("/listDictByParentId")
    public ResponseResult<List<Map<String, Object>>> listDictByParentId(@RequestParam(required = false) String parentId) {
        Collection<AreaCode> resultList = areaCodeService.getListByParentId(parentId);
        return ResponseResult.success(MyCommonUtil.toDictDataList(
                resultList, AreaCode::getAreaId, AreaCode::getAreaName, AreaCode::getParentId));
    }

    /**
     * 将当前字典表的数据重新加载到缓存中。
     * 由于缓存的数据更新，在add/update/delete等接口均有同步处理。因此该接口仅当同步过程中出现问题时，
     * 可手工调用，或者每天晚上定时同步一次。
     */
    @SaCheckPermission("globalDict.view")
    @OperationLog(type = SysOperationLogType.RELOAD_CACHE)
    @GetMapping("/reloadCachedData")
    public ResponseResult<Boolean> reloadCachedData() {
        areaCodeService.reloadCachedData(true);
        return ResponseResult.success(true);
    }
}
